package com.fujimana.android.manacode.converter;

import java.io.IOException;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.fujimana.android.manacode.converter.token.LinefeedToken;
import com.fujimana.android.manacode.converter.token.LinenoToken;
import com.fujimana.android.manacode.converter.token.LiteralToken;
import com.fujimana.android.manacode.converter.token.WordToken;

public class TokenParser {

	public final static char QUOTE = '\'';
	public final static char DOUBLE_QUOTE = '"';

	private List<TokenMatcher> matchers;

	private List<Token> tokens;
	
	private Options options;

	TokenParser(List<TokenMatcher> matchers, Options options) {
		this.tokens = new ArrayList<Token>();
		this.matchers = matchers;
		this.options = options;
	}
	
	public Options getOptions() {
		return options;
	}

	public void parse(Reader reader) throws IOException {
		StreamTokenizer tokenizer = new StreamTokenizer(reader);

		initTokenizer(tokenizer);

		int token = 0;

		tokens.add(new LinenoToken(1, getOptions()));
		while ((token = tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
			String word = null;
			
			switch (token) {
			case StreamTokenizer.TT_EOL:
				tokens.add(new LinefeedToken(getOptions()));
				tokens.add(new LinenoToken(tokenizer.lineno(), getOptions()));
				break;
			case StreamTokenizer.TT_WORD:
				tokens.add(new WordToken(tokenizer.sval,getOptions()));
				break;
			case QUOTE:
				tokens.add(new LiteralToken("'" + tokenizer.sval + "'",getOptions()));
				break;
			case DOUBLE_QUOTE:
				tokens.add(new LiteralToken('"' + tokenizer.sval + '"',getOptions()));
				break;
			default:
				word = "" + (char) tokenizer.ttype;

				TokenMatcher hit = null;
				TokenAccessor accessor = new TokenAccessor(tokenizer);
				for (TokenMatcher matcher : this.matchers) {
					if (matcher.isMatch(word, accessor)) {
						hit = matcher;
					} else {
						accessor.cancel();
					}
				}

				if (hit == null) {
					tokens.add(new WordToken(word, getOptions()));
				} else {
					String str = hit.getMatchString();
					int index = -1;
					while( (index = str.indexOf('\n')) != -1) {
						if(index != 0) {
							String aaa = str.substring(0, index);
							tokens.add(hit.getToken(aaa));
							tokens.add(new LinefeedToken(getOptions()));
							tokens.add(new LinenoToken(tokenizer.lineno(), getOptions()));
						}
						
						str = str.substring(index+1);
					}
					
					tokens.add(hit.getToken(str));
				}
			}
		}
	}

	public List<Token> getTokens() {
		return Collections.unmodifiableList(this.tokens);
	}

	private void initTokenizer(StreamTokenizer tokenizer) {
		tokenizer.resetSyntax();
		tokenizer.wordChars('0', '9');
		tokenizer.wordChars('a', 'z');
		tokenizer.wordChars('A', 'Z');
		tokenizer.wordChars('_', '_');
		tokenizer.whitespaceChars('\n', '\n');
		tokenizer.whitespaceChars('\r', '\r');
		tokenizer.quoteChar(QUOTE);
		tokenizer.quoteChar(DOUBLE_QUOTE);
		tokenizer.eolIsSignificant(true);
	}
	
	
}
